%{
/***************************************************************************
                         dir.c  --  directive parser
                             -------------------
    begin                : Wed Jun 14 17:58:28 BST 2000
    copyright            : (C) 2000 by Simon Collis
    email                : scollis@angelfire.com
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public Licence as published by  *
 *   the Free Software Foundation; either version 2 of the Licence, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

/*===========================================================================*
 * If you are reading dir.c, don't edit it.  Edit dir.sh instead!!!          *
 *===========================================================================*/

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "a6.h"
#include "asm.h"
#include "err.h"
#include "exp.h"
#include "global.h"
#include "lbl.h"
#include "outf.h"
#include "pstext.h"
#include "system.h"
#include "ustring.h"

/******************************************************************************
  Maintenance note:
	functions which have not significantly changed retain the old
	names, e.g. "dotcset".  Functions which have been rewritten with
	Piper in mind are named d_, such as "d_byte".
 *****************************************************************************/

/*----------------------------------------------------------------------------*
 * d_incbin: handle binary includes                                           *
 *----------------------------------------------------------------------------*/
void d_incbin(char *name, int fileformat, char *s)
{
	FILE *infile;
	unsigned char c;

	s = extractfilename(name);

	/* Open file for reading */
	infile=fopen(s,"rb");

	/* Error if file not found */
	if(infile==NULL) {
		error(ERR_FILENOTFOUND,s,ERR_PASS2);
		return;
	}

	/* Skip file header and copy file to output */
	switch(fileformat) {
		case OUTF_PRG:
			getc(infile);
			getc(infile);
			/* DELIBERATE DROPTHROUGH!!! */

		case OUTF_BIN:
			c=getc(infile);
			while(!feof(infile)) {
				outf_wbyte(c);
				c=getc(infile);
			}
			break;

		case OUTF_P00:
			for(c=0;c<0x1a && !feof(infile);c++)
				getc(infile);
			while(!feof(infile)) {
				outf_wbyte(c);
				c=getc(infile);
			}
	}

	/* Close input file */
	fclose(infile);
}


/*---------------------------------------------------------------------------*
 * d_byte : .byte, .word, .text etc handler                                  *
 * Flags are:                                                                *
 *   0x03 = argsize 0=1 byte, 1=2 bytes, 2=3 bytes, 3=4 bytes                *
 *   0x0C = endian  0=little, 1=big, 2=PDP, 3=CPU                            *
 *   0x10 = .shift                                                           *
 *   0x20 = .null                                                            *
 *---------------------------------------------------------------------------*/
void d_byte(char *e, int flags, char *t)
{
	int size = (flags & 0x03);	/* 00-03=size */
	int endian = ((flags >> 2) & 0x03);
	int quote, chr;
	unsigned long val;

	/* Loop through the expressions */
	while(e != NULL && *e) {
		/* Skip leading whitespace */
		while(isspace(*e)) e++;

		/* Text argument? */
		if(*e == '\'' || *e == '\"') {
			quote = *e++;

			/* Loop until end of text */
			while(quote && *e) {
				/* Check if current char is quote */
				if(*e == quote) {
					if(e[1] == quote) {
						e += 2;
						chr = quote;
					} else {
						quote = 0;
						chr = -1;
					}
				} else
					chr = *e;

				/* Next character */
				e++;

				/* Convert current */
				if(chr != -1) {
					chr = pstext_convchar(chr);

					/* Scan ahead for end of text */
					if((flags & 0x10) && (*e == '\0' || (*e == quote && e[1] != quote)))
						chr |= 0x80;

					/* Output the character */
					outf_wbyte(chr);
				}
			}

			/* Output null char? */
			if(flags & 0x20)
				outf_wbyte(0);

			/* Skip whitespace after string */
			while(isspace(*e)) e++;

			/* Skip comma */
			while(*e == ',') e++;

		/* Expression argument */
		} else {
			/* Find next argument */
			t = strchr(e, ',');
			if(t != NULL)
				*t++ = '\0';

			/* Get the value */
			val = psexpr(e);

			/* Warn if value too large (on second pass) */
			if(g_pass != 0) {
				if(*e == '-' || *e == '~') {
					if((size == 0 && val > 0x00007f && val < 0xffffff80) ||
					   (size == 1 && val > 0x007fff && val < 0xffff8000) ||
					   (size == 2 && val > 0x7fffff && val < 0xff800000)) {
						printf("%u %u %s",size,val,e);
						error(ERR_ARGTOOBIG, e, ERR_WARNING);
					}
				} else {
					if((size == 0 && val > 0x0000ff) ||
					   (size == 1 && val > 0x00ffff) ||
					   (size == 2 && val > 0xffffff)) {
						printf("%u %u %s", size, val, e);
						error(ERR_ARGTOOBIG, e, ERR_WARNING);
					}
				}
			}

			/* Write the data */
			outf_wdata(val, size, endian);

			/* Next expression */
			e = t;
		}
	}
}

/*---------------------------------------------------------------------------*
 * d_cpu: load a CPU definition file                                         *
 *---------------------------------------------------------------------------*/
void d_cpu(char *expr, int flags, char *t)
{
	cpu_load(expr);
}

/*---------------------------------------------------------------------------*
 * d_block: handle .block, .pad                                              *
 *---------------------------------------------------------------------------*/
void d_block(char *e, int flags, char *t)
{
	unsigned long val=psexpr(e);

	val=val & 0xff;

	while(val) {
		outf_wbyte(cpu_getnop());
		val--;
	}
}

/*---------------------------------------------------------------------------*
 * d_include : source code inclusions (.file, .include, .lib)                *
 *---------------------------------------------------------------------------*/
void d_include(char *expr, int flags, char *t)
{
	FILE *f = NULL;
	char *s = trim(expr);
	char *u, *p;
	int scancurrent = 1;

	t = NULL;

	/* strip single quote */
	if(*s == '\'') {
		t = strchr(s + 1, '\'');
		if(t == NULL) {
			error(ERR_UNTERMSTRING,s,ERR_PASS1);
			return;
		}

	} else if(*s == '\"') {
		t = strchr(s + 1, '\"');
		if(t == NULL) {
			error(ERR_UNTERMSTRING,s,ERR_PASS1);
			return;
		}

	} else if(*s == '<') {
		t = strchr(s + 1,'>');
		if(t == 0) {
			error(ERR_UNTERMSTRING,s,ERR_PASS1);
			return;
		}
		scancurrent=0;
	}

	if(t) {
		*t=0;
		s++;
	}

	if(scancurrent) {
		f = fopen(s, "r");
	}

	if(f != NULL) {
		af_open(s, f);
		return;
	}

	t = getenv("A6_INCPATH");

	p = malloc(256 * sizeof(char));

	/* Close current file if this is a .file */
	if(flags & 0x01)
		af_close();

	/* Find the file */
	while(t) {
		/* Find where current section of path ends */
		u = strchr(t,PATH_SEPARATOR);

		/* Delete separator */
		if(u)
			*u++ = 0;

		/* Build new filename */
		scancurrent = strlen(t);

		if(t[scancurrent - 1] == PATH_SLASH)
			sprintf(p, "%s%s", t, s);
		else
			sprintf(p, "%s%c%s", t, PATH_SLASH, s);

		/* Find it */
		if((f = fopen(p,"r"))) {
			af_open(p, f);
			free(p);
			return;
		}

		/* Next path */
/*		if(u)*/
			t=u;
	}

	free(p);

	error(ERR_FILENOTFOUND, s, ERR_PASS2);
}

/*---------------------------------------------------------------------------*
 * d_if: .if, .ifneq, etc etc etc                                            *
 *---------------------------------------------------------------------------*/
void d_if(char *e, int flags, char *t)
{
	register long val = (long)psexpr(e);

	if((flags & 0x10) == 0) {
		switch(flags & 0x03) {
			case 0:
				af_entercondasm(val == 0);
				break;

			case 1:
				af_entercondasm((val & 1) == 0);
				break;

			case 2:
				af_entercondasm(val >= 0);
				break;
		}
	} else {
		switch(flags & 0x03) {
			case 0:
				af_entercondasm(val != 0);
				break;

			case 1:
				af_entercondasm((val & 1) == 1);
				break;

			case 2:
				af_entercondasm(val < 0);
				break;
		}
	}
}

/*---------------------------------------------------------------------------*
 * d_else: flip between assembly on / off                                    *
 *---------------------------------------------------------------------------*/
void d_else(char *e, int flags, char *t)
{
	af_flipcondasm();
}

/*---------------------------------------------------------------------------*
 * d_endif: back out of conditional assembly                                 *
 *---------------------------------------------------------------------------*/
void d_endif(char *e, int flags, char *t)
{
	af_exitcondasm();
}

/*---------------------------------------------------------------------------*
 * d_echo: parrot to screen                                                  *
 *---------------------------------------------------------------------------*/
void d_echo(char *e, int flags, char *t)
{
	if(g_pass != 0)
		printf("%s\n",e);
}

/*---------------------------------------------------------------------------*
 * d_equ: synonym for = to define label                                      *
 *---------------------------------------------------------------------------*/
void d_equ(char *expr, int flags, char *lbl)
{
	lbl_define(lbl, psexpr(expr), LBL_NONE);
}

/*--------------------------------------------------------------------------*/
void dottasc(char *e, int flags, char *t)
{
	int i=pstext_getcset();
	pstext_setcset(CSET_ASCII);
	d_byte(e, 0, t);
	pstext_setcset(i);
}

void dottpet(char *e, int flags, char *t)
{
	int i=pstext_getcset();
	pstext_setcset(CSET_PETSCII);
	d_byte(e, 0, t);
	pstext_setcset(i);
}

void dottscrl(char *e, int flags, char *t)
{
	int i=pstext_getcset();
	pstext_setcset(CSET_SCRL);
	d_byte(e, 0, t);
	pstext_setcset(i);
}

void dottscru(char *e, int flags, char *t)
{
	int i=pstext_getcset();
	pstext_setcset(CSET_SCRU);
	d_byte(e, 0, t);
	pstext_setcset(i);
}

/*-----------------------------------------------------------------*/
/* Charset */
void dotcset(char *e, int flags, char *t)
{
	if(!strcmp(e,"ascii") || !strcmp(e,"host")) pstext_setcset(CSET_ASCII);
	else if(!strcmp(e,"scru")) pstext_setcset(CSET_SCRU);
	else if(!strcmp(e,"scrl")) pstext_setcset(CSET_SCRL);
	else if(!strcmp(e,"pet")) pstext_setcset(CSET_PETSCII);
	else error(ERR_UNKNOWNCSET,e,ERR_PASS2);
}

/*-----------------------------------------------------------------*/
/* Local scopes */
void dotendloc(char *expr, int flags, char *t)
{
	lbl_exitlocal();
}

void dotlocal(char *expr, int flags, char *t)
{
	lbl_enterlocal();
}

/*-----------------------------------------------------------------*/
/* Org */
void dotorg(char *e, int flags, char *t)
{
	if(outf_getpc() != 0)
		error(ERR_SECONDDOTORG,NULL,ERR_PASS2);
	else {
		outf_setpc(psexpr(e));
	}
}

/*-----------------------------------------------------------------*/
/* Align */
void dotalign(char *e, int flags, char *t)
{
	unsigned int alignto = psexpr(e) & 0xff;

	if(alignto == 0) alignto=256;

	while(outf_getpc() & alignto)
		outf_wbyte(0xea);
}

/*-----------------------------------------------------------------*/
/* Ver */

/* This has changed significantly for 0.4.4 (Caleb) and beyond.
   Main version is taken from global.h */
void dotver(char *e, int flags, char *t)
{
	int fail = 0;
	unsigned int ver = psexpr(e);

	/* Fix for documentation supplied with 0.4.3 */
	if(ver == 0x403)
		ver = 43;

	/* Check requested version later than this one */
	if(ver > G_VER) {
		printf("A6 version %u.%u.%u is recommended to assemble this source!\n",ver/100,(ver/10)%10,ver%10);

		if(ver>G_FAILVER)
			printf("Fatal error:  Later version of A6 requested.\n\n"
				"Go to http://www.angelfire.com/my/a6/ to download the latest version.\n");
	}

	/* Check for <0.5.0 */
	if(ver < 50) {
		printf("This is a pre-0.5.0 source; you'll need to change the .ver and add\n"
			"        .cpu 6510\n"
			"at the top of the file in order to compile it.\n");
		fail++;
	}

	if(fail)
		exit (EXIT_FAILURE);
}

/*-----------------------------------------------------------------*/
/* .ADD, .EOR */
void dotadd(char *e, int flags, char *t)
{
	if(g_pass)
		g_outf_add=psexpr(e);
}

void doteor(char *e, int flags, char *t)
{
	if(g_pass)
		g_outf_eor=psexpr(e);
}

/*-----------------------------------------------------------------*/
/* Undocumented opcodes on and off */
void dotundoc(char *e, int flags, char *t)
{
	g_undocopsflag = 1;
}

void dotnoundoc(char *e, int flags, char *t)
{
	g_undocopsflag = 0;
}

/*---------------------------------------------------------------------------*
 *  directives                                                               *
 *    flags:                                                                 *
 *      0x10 = don't autodefine label                                        *
 *	0x02 = this is a conditional					     *
 *      0x01 = parse when assembly off                                       *
 *      0xffff00>>8 = flags for routine                                      *
 *---------------------------------------------------------------------------*/

typedef void (*PFC)(char *, int, char *);

%}
struct directive { char *name; PFC function; int flags; } ;
%%
add,	dotadd,		0
align,	dotalign,	0
block,	d_block,	0x0100
byt,	d_byte,		0x0000
byte,	d_byte,		0x0000
cpu,	d_cpu,		0x0000
cset,	dotcset,	0
echo,	d_echo,		0
else,	d_else,		0x0001
endif,	d_endif,	0x0001
endloc,	dotendloc,	0
eor,	doteor,		0
equ,	d_equ,		0x0010
file,	d_include,	0x0100
if,	d_if,		0x1002
ifend,	d_endif,	0x0001
ifequ,	d_if,		0x0002
ifeven,	d_if,		0x0102
ifmi,	d_if,		0x1202
ifneg,	d_if,		0x1202
ifneq,	d_if,		0x1002
ifodd,	d_if,		0x1102
ifpl,	d_if,		0x0202
ifpos,	d_if,		0x0202
inc,	d_include,	0x0000
incbin,	d_incbin,	0x0000
include,d_include,	0x0000
incp00,	d_incbin,	0x0200
intprg,	d_incbin,	0x0100
lib,	d_include,	0x0000
local,	dotlocal,	0
long,	d_byte,		0x3300
noundoc,dotnoundoc,	0
null,	d_byte,		0x2000
org,	dotorg,		0
pad,	d_block,	0x0000
shift,	d_byte,		0x1000
tasc,	dottasc,	0
text,	d_byte,		0x0000
tpet,	dottpet,	0
tscrl,	dottscrl,	0
tscru,	dottscru,	0
txt,	d_byte,		0x0000
undoc,	dotundoc,	0
ver,	dotver,		0
wor,	d_byte,		0x0100
word,	d_byte,		0x0100
%%

/* --------------------------------------------------------------- */
/* Parse directive                                                 */
void psdir(char *popn, char *expr, char *label)
{
	const struct directive *pop;
	PFC z;

	pop = in_word_set(popn, strlen(popn));

	/* Return if not in assembly */
	if((cond_in_asm == 0) && ((pop->flags & 0x3) == 0))
		return;

	/* Found the directive? */
	if(pop == NULL) {
		if(cond_in_asm != 0) {
			error(ERR_UNKNOWNDIRECTIVE, popn, ERR_PASS2);
			if(label != NULL)
				lbl_define(label, outf_getpc(), LBL_SYNCPC);
		}
	} else {
		/* Define label? */
		if(cond_in_asm != 0) {
			if((label != NULL) && ((pop ->flags & 0x10) == 0))
				lbl_define(label, outf_getpc(), LBL_SYNCPC);

			z = pop->function;
			z(expr, (pop->flags >> 8), label);
		} else {
			switch(pop->flags & 0x03) {
				case 1:
					z = pop->function;
					z(expr, (pop->flags >> 8), label);
					break;

				case 2:
					af_entercondasm(0);
					break;
			}
		}
	}
}
